package com.guosen.zebra.gateway.handler;

import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Throwables;
import com.guosen.zebra.core.exception.RpcServiceException;
import com.guosen.zebra.core.grpc.anotation.ZebraReference;
import com.guosen.zebra.core.grpc.server.GenericService;
import com.guosen.zebra.gateway.exception.RouteNotFound;
import com.guosen.zebra.gateway.exception.ServiceNotFound;
import com.guosen.zebra.gateway.route.model.RouteInfo;
import com.guosen.zebra.gateway.route.router.Router;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Map;

import static io.grpc.Status.Code.UNIMPLEMENTED;

/**
 * Zebra服务请求处理器
 */
@Component
public class ZebraServiceHandler {
    private static final Logger LOGGER = LoggerFactory.getLogger(ZebraServiceHandler.class);

    @Autowired
    private Router router;

    @ZebraReference
    private GenericService genericService;

    /**
     * 处理请求
     * @param requestURI        请求URI（去除掉前缀的URI）
     * @param requestParameters 请求参数
     * @param headers           HTTP header
     * @return  处理结果
     * @throws RouteNotFound    未找到路由
     * @throws ServiceNotFound  未找到服务
     */
    public JSONObject handleRequest(String requestURI, JSONObject requestParameters, Map<String, String> headers)
            throws RouteNotFound, ServiceNotFound {
        RouteInfo routeInfo = router.route(requestURI);
        if (routeInfo == null) {
            LOGGER.debug("Could not find route info for URI [{}]", requestURI);
            throw new RouteNotFound();
        }

        return invoke(routeInfo, requestParameters, headers);
    }

    private JSONObject invoke(RouteInfo routeInfo, JSONObject requestParameter, Map<String, String> headers) throws ServiceNotFound {
        String serviceName = routeInfo.getServiceName();
        String group = routeInfo.getGroup();
        String version = routeInfo.getVersion();
        String method = routeInfo.getMethod();

        JSONObject result = null;
        try {
            result = genericService.$invoke(serviceName, group, version, method, requestParameter);
        } catch (RpcServiceException e) {
            handleRpcServiceException(e);
        }

        return result;
    }

    private void handleRpcServiceException(RpcServiceException e) throws ServiceNotFound {
        Throwable rootCause = Throwables.getRootCause(e);
        if (rootCause instanceof StatusRuntimeException) {
            Status status = ((StatusRuntimeException)rootCause).getStatus();
            if (status.getCode() == UNIMPLEMENTED) {
                throw new ServiceNotFound();
            }
        }

        throw e;
    }
}
